import { isOfficialShard, isRoomInterval, roomLog } from '@/ayaka'
import SpawnController, { getCreeps } from '@/modules/structures/spawn'
import { CreepRoles, SpawnTask } from '@/modules/structures/spawn/types'
import { BodyPartArrayType } from '@/utils/common'
import { createUpgraderBody, upgraderWork } from './upgrader'
import { UpgradeMemory } from './types'
import { getFreeSpace } from '@/utils/position'
import { createTransporterBody } from '../transport/transporter'
import { upgradeCarrierWork } from './upgradeCarrier'
import { RoomSafeState } from '@/room/state/types'

/**
 * 升级任务
 */
export default class UpgradeTaskManager {
    /**
     * 中心点到控制器的距离缓存
     */
    public static centerDistanceCache: { [roomName: string]: number } = {}

    /**
     * 不可实例化
     */
    protected constructor() {
        throw new Error('UpgradeTaskManager 不可被实例化！')
    }

    /**
     * 获取 memory
     */
    public static getMemory(room: MyRoom) {
        if (!room.memory.upgrade) {
            room.memory.upgrade = {
                upgrader: [],
                upgradeCarrier: [],
                containerIds: [],
                // 留一个位置给搬运工
                freePos: getFreeSpace(room.controller.pos, { range: 3 }).length - 1,
            }
        }
        if (!room.memory.upgrade.sourceDistance) {
            room.memory.upgrade.sourceDistance = UpgradeTaskManager.getSourceDistance(room)
        }
        return room.memory.upgrade
    }

    /**
     * 运行升级任务
     */
    public static run(room: MyRoom) {
        const memory = UpgradeTaskManager.getMemory(room)

        const upgraders = UpgradeTaskManager.getUpgrader(room, memory.upgrader)
        UpgradeTaskManager.spawnUpgrader(room, upgraders, memory)
        upgraderWork(room, upgraders, memory)

        const upgradeCarriers = UpgradeTaskManager.getUpgradeCarrier(room, memory.upgradeCarrier)
        UpgradeTaskManager.spawnUpgradeCarrier(room, upgradeCarriers, upgraders, memory)
        upgradeCarrierWork(room, upgradeCarriers, memory)
    }

    /**
     * 判断能否并孵化新的 upgrader
     */
    protected static spawnUpgrader(room: MyRoom, creeps: Creep[], memory: UpgradeMemory) {
        if (!isRoomInterval(room, 61)) return

        if (SpawnController.searchSpawnTaskByRole(room, CreepRoles.upgrader).length) return

        creeps = creeps.filter((creep) => {
            return (
                !creep.ticksToLive ||
                creep.ticksToLive > Math.max(creep.memory.travelTime || 20, 20) + creep.body.length * 3
            )
        })

        const controller = room.controller
        if (controller.level >= 8 && creeps.length) return

        if (creeps.length >= memory.freePos) return

        let require = controller.ticksToDowngrade < CONTROLLER_DOWNGRADE[controller.level] / 2 + 5000 ? 1 : 0

        if (!require && room.memory.state.safe?.state !== RoomSafeState.normal) return

        if (room.controller.level === 8 && !require && Memory.meta.economy) return

        if (room.storage?.my || room.terminal?.my) {
            const energy = (room.storage?.store['energy'] || 0) + (room.terminal?.store['energy'] || 0)
            require = Math.floor(energy / (UpgradeTaskManager.isQuickUpgrade(room) ? 30e3 : 1e5)) || require
        } else {
            const upgradeContainers = Object.keys(room.memory.structures.container || {})
                .map((id) => Game.getObjectById(id) as StructureContainer)
                .filter(Boolean)
            const energy = upgradeContainers.reduce((total, container) => total + container.store['energy'], 0)
            // 多孵化一个
            if (energy > 1200 * upgradeContainers.length && memory.containerIds?.length) {
                require = creeps.length + 1
            }
        }

        if (creeps.length >= require) return

        UpgradeTaskManager.addUpgradeSpawnTask(room, memory.upgrader, createUpgraderBody(room))
    }

    /**
     * 获取 upgrader
     */
    protected static getUpgrader(room: MyRoom, creepNames: string[]) {
        return getCreeps(room, creepNames)
    }

    /**
     * 添加 upgrader 孵化任务
     */
    protected static addUpgradeSpawnTask(room: MyRoom, creepNames: string[], body: BodyPartArrayType) {
        const task: SpawnTask = {
            role: CreepRoles.upgrader,
            body,
            defaultAdaption: true,
        }

        // 6级以上考虑 boost
        if (room.controller.level >= 6) {
            for (const type of ['XGH2O', 'GH2O', 'GH'] as const) {
                // 到 8 级了就省点花用 GH
                if (type !== 'GH' && room.controller.level >= 8) continue
                if (
                    room.storage &&
                    room.storage.store['energy'] + (room.terminal?.store['energy'] || 0) > 60000 &&
                    room.storage.store[type] > 5000 &&
                    Object.keys(room.memory.lab.inLab).length
                ) {
                    task.memory = {
                        boost: {
                            data: {
                                work: {
                                    type: type,
                                    require: 50,
                                    amount: 0,
                                },
                            },
                        },
                    }
                    break
                }
            }
        }

        const creepName = SpawnController.addSpawnTask(room, task)
        if (!creepName) return

        creepNames.push(creepName)
    }

    /**
     * 判断能否并孵化新的 upgradeCarrier
     */
    protected static spawnUpgradeCarrier(room: MyRoom, creeps: Creep[], upgraders: Creep[], memory: UpgradeMemory) {
        if (!isRoomInterval(room, 63)) return

        // 能量缺乏就不孵化或者等于 8级
        if (room.controller.level === 8 || room.memory.state.energy || !memory.containerIds?.length) return

        if (SpawnController.searchSpawnTaskByRole(room, CreepRoles.upgradeCarrier).length) return

        creeps = creeps.filter((creep) => {
            return (
                !creep.ticksToLive ||
                creep.ticksToLive > UpgradeTaskManager.getDistance(room) * 2 + creep.body.length * 3
            )
        })

        if (creeps.length >= 6 || !memory.upgrader.length) return

        if (room.storage?.my || room.terminal?.my) {
            // 如果仓库在控制器周围，说明是借助仓库的能量而不是 container，就不需要 carrier
            if (room.storage && room.storage.pos.getRangeTo(room.controller) <= 2) return
            if (room.terminal && room.terminal.pos.getRangeTo(room.controller) <= 2) return

            if (
                creeps.reduce((acc, c) => acc + c.getActiveBodyparts(CARRY), 0) * 50 >=
                upgraders.reduce((acc, c) => acc + c.getActiveBodyparts(WORK), 0) *
                    2 *
                    UpgradeTaskManager.getDistance(room)
            ) {
                return
            }
        } else if (creeps.length >= 2) {
            const sourceDistance = memory.sourceDistance || 40
            if (
                creeps.reduce((acc, c) => acc + c.getActiveBodyparts(CARRY), 0) * 50 >=
                upgraders.reduce((acc, c) => acc + c.getActiveBodyparts(WORK), 0) * 2 * sourceDistance
            ) {
                return
            }

            const sourceContainers = Object.values(room.memory.source)
                .map(({ containerId }) => containerId && Game.getObjectById(containerId))
                .filter(Boolean) as StructureContainer[]
            const energy = sourceContainers.reduce((total, container) => total + container.store['energy'], 0)
            if (energy < 1400 * sourceContainers.length) return
        }

        UpgradeTaskManager.addUpgradeCarrierSpawnTask(room, memory.upgradeCarrier, createTransporterBody(room))
    }

    /**
     * 获取 upgradeCarrier
     */
    protected static getUpgradeCarrier(room: MyRoom, creepNames: string[]) {
        return getCreeps(room, creepNames)
    }

    /**
     * 添加 upgradeCarrier 孵化任务
     */
    protected static addUpgradeCarrierSpawnTask(room: MyRoom, creepNames: string[], body: BodyPartArrayType) {
        const task: SpawnTask = {
            role: CreepRoles.upgradeCarrier,
            body,
            defaultAdaption: true,
        }
        const creepName = SpawnController.addSpawnTask(room, task)
        if (!creepName) return

        creepNames.push(creepName)
    }

    /**
     * 注册 container
     */
    public static registerContainer(room: MyRoom, containers: StructureContainer[]): StructureContainer[] {
        const memory = UpgradeTaskManager.getMemory(room)
        memory.containerIds = []

        return containers.filter((container) => {
            if (room.controller.pos.getRangeTo(container) <= 3) {
                memory.containerIds.push(container.id)
                return false
            }
            return true
        })
    }

    /**
     * 注册 link
     */
    public static registerLink(room: MyRoom, links: StructureLink[]) {
        const memory = UpgradeTaskManager.getMemory(room)
        delete memory.linkId

        return links.filter((link) => {
            if (!memory.linkId && room.controller.pos.getRangeTo(link) <= 3) {
                memory.linkId = link.id
                return false
            }
            return true
        })
    }

    /**
     * 获取中心点到控制器的距离
     */
    public static getDistance(room: MyRoom) {
        if (!room.memory.center) return 25
        if (!UpgradeTaskManager.centerDistanceCache[room.name]) {
            const centerPos = new RoomPosition(room.memory.center[0], room.memory.center[1], room.name)
            UpgradeTaskManager.centerDistanceCache[room.name] =
                centerPos.findPathTo(room.controller.pos, { ignoreCreeps: true }).length || 25
            roomLog(room.name, `中心点到控制器的距离为 ${UpgradeTaskManager.centerDistanceCache[room.name]}`)
        }
        return UpgradeTaskManager.centerDistanceCache[room.name]
    }

    /**
     * 获取能量矿到控制器的距离
     */
    public static getSourceDistance(room: MyRoom) {
        if (!room.memory.source) return
        let distance = 0
        for (const sourceId of Object.keys(room.memory.source)) {
            const source = Game.getObjectById(sourceId as Id<Source>)
            if (!source) continue
            distance += source.pos.findPathTo(room.controller.pos).length || 20
        }
        roomLog(room.name, `能量矿到控制器的距离之和为 ${distance}`)
        return distance
    }

    /**
     * 房间是否处于冲击模式
     */
    public static isQuickUpgrade(room: MyRoom) {
        return (
            room.terminal &&
            (room.controller.level === 6 || room.controller.level == 7) &&
            (Game.market.credits > 30e6 || (!isOfficialShard() && Game.market.credits > 10e3)) &&
            Game.flags[`quickUpgrade_${room.name}`]
        )
    }
}
